﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace crdmcgui
{
    [Serializable]
    public class Ruleset : IEquatable<Ruleset>
    {
        private string _name;
        private string _original;

        private const string DefaultRuleset = "<<Series.Is:Batman>> => <<SeriesGroup.SetValue:Batman>>";

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }
        public string QuickView
        {
            get
            {
                return ToString();
            }
        }
        public string Comment { get; set; }
        public string OriginalText 
        { 
            get
            {
            if (string.IsNullOrEmpty(_original))
                return QuickView; 
            else
                return _original;
            }
            set
            {                
                _original = value;
            } 
        }

        public SortableBindingList<DataManagerParameters> Rules { get; set; }
        public SortableBindingList<DataManagerParameters> Actions { get; set; }

        public Ruleset()
        {
            Clear();
        }

        public Ruleset(string strRule)
            : this()
        {
            Parse(strRule);
        }

        public Ruleset(string[] strStrings)
            :this() 
        {
            if (strStrings.Length > 1)
            {

                string[] NameAndComment = strStrings[0].Split(new string[] { Global.RulesetNameHeader, Global.CommentHeader }, StringSplitOptions.RemoveEmptyEntries);
                if (NameAndComment.Length > 0)
                    Name = NameAndComment[0];
                if (NameAndComment.Length > 1)
                    Comment = NameAndComment[1];
                Parse(strStrings[1]);
            }
            else
                Parse(strStrings[0]);
        }

        public Ruleset(Ruleset other)
            :this()
        {
            foreach (DataManagerParameters item in other.Rules)
            {
                this.Rules.Add(item.Clone());
            }
            foreach (DataManagerParameters item in other.Actions)
            {
                this.Actions.Add(item.Clone());
            }
        }

        private void Clear()
        {
            this._name = "";
            this.Comment = "";
            this.Actions = new SortableBindingList<DataManagerParameters>();
            this.Rules = new SortableBindingList<DataManagerParameters>();
        }

        public void Parse(string strRule)
        {
            this.Actions = new SortableBindingList<DataManagerParameters>();
            this.Rules = new SortableBindingList<DataManagerParameters>();

            this.OriginalText = strRule;

            string[] tmpString = strRule.Split(new string[] { " => ", " =>", "=> ", "=>" }, StringSplitOptions.RemoveEmptyEntries);
            try
            {
                parseRules(tmpString[0]);
                parseActions(tmpString[1]);
                OriginalText = "";
            }
            catch { }
        }

        private void parseRules(string strRulesToParse)
        {
            if (strRulesToParse.StartsWith("<<"))
            {
                string[] strArrayRules = strRulesToParse.Trim().Split(new string[] { ">> ", ">>", " <<", "<<" }, StringSplitOptions.RemoveEmptyEntries);

                for (int i = 0; i < strArrayRules.Length; i++)
                {
                    if (strArrayRules[i].Trim() != "")
                    {
                        string sRuleToParse = strArrayRules[i];

                        string[] strArrayRule = sRuleToParse.Split(new string[] { "<<", ">>", ":" }, 2, StringSplitOptions.RemoveEmptyEntries);

                        string[] strRule = strArrayRule[0].Trim().Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);

                        DataManagerParameters tmpRule = new DataManagerParameters(ParameterType.Rule);
                        tmpRule.Field = strRule[0];
                        if (strRule.Length > 1)
                        {
                            tmpRule.Modifier = strRule[1];

                        }
                        else if (strRule.Length < 2)
                        {
                            tmpRule.Modifier = "Is";
                        }
                        if (strArrayRule.Length > 1)
                            tmpRule.Value = strArrayRule[1];
                        else
                            tmpRule.Value = "";
                        Rules.Add(tmpRule);
                    }
                }
            }
            else throw new Exception("Syntax error");
        }

        private void parseActions(string p)
        {
            string[] strArrayActions = p.Trim().Split(new string[] { ">>", "<<" }, StringSplitOptions.RemoveEmptyEntries);

            for (int i = 0; i < strArrayActions.Length; i++)
            {
                if (strArrayActions[i].Trim() != "")
                {
                    string sActionToParse = strArrayActions[i];

                    string[] strArrayAction = sActionToParse.Split(new string[] { "<<", ">>", ":" }, 2, StringSplitOptions.RemoveEmptyEntries);

                    string[] strAction = strArrayAction[0].Trim().Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);

                    DataManagerParameters tmpAction = new DataManagerParameters(ParameterType.Action);
                    tmpAction.Field = strAction[0];
                    if (strAction.Length > 1)
                    {
                        tmpAction.Modifier = strAction[1];

                    }
                    else if (strAction.Length < 2)
                    {
                        tmpAction.Modifier = "SetValue";
                    }
                    if (strArrayAction.Length > 1)
                        tmpAction.Value = strArrayAction[1];
                    else
                        tmpAction.Value = "";
                    Actions.Add(tmpAction);
                }
            }
        }

        public override string ToString()
        {
            string strReturn = "";

            if (Rules.Count < 1 || Actions.Count < 1)
            {
                strReturn = "#@ Invalid Ruleset Reason: ";

                if (Rules.Count < 1 && Actions.Count < 1)
                    strReturn += "There are no rules or actions defined.";
                else if (Rules.Count < 1)
                    strReturn += "There are no Rules defined.";
                else if (Actions.Count < 1)
                    strReturn += "There are no Actions defined.";
            }

            for (int i = 0; i < Rules.Count; i++)
            {
                strReturn += Rules[i].Compile() + " ";
            }

            strReturn += "=> ";

            for (int i = 0; i < Actions.Count; i++)
            {
                strReturn += Actions[i].Compile();
                if (i != Actions.Count - 1)
                {
                    strReturn += " ";
                }
            }
            return strReturn;
        }

        public string[] Compile()
        {
            List<string> CompiledRuleset = new List<string>();
            if (!string.IsNullOrEmpty(_name))
            {
                string tmpString = Global.RulesetNameHeader + Name;
                if (!string.IsNullOrEmpty(Comment))
                {
                    tmpString += " " + Global.CommentHeader + Comment;
                }
                CompiledRuleset.Add(tmpString);
            }

            CompiledRuleset.Add(ToString());
            return CompiledRuleset.ToArray();
        }

        public Ruleset Clone()
        {
            Ruleset tmp = new Ruleset();
            tmp.Name = this.Name;
            tmp.Comment = this.Comment;

            foreach (DataManagerParameters item in Rules)
            {
                tmp.Rules.Add((DataManagerParameters)item.Clone());
            }

            foreach (DataManagerParameters item in Actions)
            {
                tmp.Actions.Add((DataManagerParameters)item.Clone());
            }

            return tmp;
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() == this.GetType())
            {

            }
            return this.Equals((Ruleset)obj);
        }

        public override int GetHashCode()
        {
            string strHashCalculator = this.Name + this.Comment + ToString();
            int tmpHash = strHashCalculator.GetHashCode();
            return tmpHash;
        }

        public bool Equals(Ruleset other)
        {
            int thisHash = GetHashCode();
            int otherHash = other.GetHashCode();
            return thisHash == other.GetHashCode();
        }

        internal void Reparse()
        {
            try
            {
                Parse(OriginalText);
            }
            catch { }
        }
    }
}
